StateモナドをIndexed Stateモナドにしていく
通常のMonadとIndexed Monad、
を比べて見る
通常のMonadの定義
code:hs
class Monad m where
return :: a -> m a
bind :: m a -> (a -> m b) -> m b
Indexed Monadの定義
code:hs
class IxMonad md where
ireturn :: a -> md i i a
ibind :: md i j a -> (a -> md j k b) -> md i k b
通常のMonadは、IxMonadで表現することができる
IxMonadはMonadの一般化なので。
code:hs
newtype M m p q a = M { unM:: m a }
instance Monad m => IxMonad (M m) where
ireturn = M . return
ibind (M m) f = M (m >>= unM . f)
通常のState Monadの使用例
状態の初期値に0を取って、内部で1増やしている
code:hs
test1 :: (Int, Int)
test1 = runState c 0
where
c = do
v <- get
put $ succ v
return v
-- (0, 1)
doの糖衣構文を解く
上のtest1をdoを使わずに書く
code:hs
test1' :: (Int, Int)
test1' = runState c 0
where
c = get bind (
\v -> put (succ v) bind (
\_ -> return v))
-- (0, 1)
Indexed Monadを使って通常のState Monadを使用する
IxMonadで作ったStateモナド用に、iget、iputを定義しておく
code:hs
iget :: (MonadState s m) => M m s s s
iget = M get
iput :: (MonadState s m) => s -> M m s s ()
iput = M . put
通常のStateモナドの使用例を、IxMonadを使って書いている
code:hs
test2 :: (Int, Int)
test2 = runState (unM c) 0
where
c = iget ibind (
\v -> iput (succ v) ibind (
\_ -> ireturn v))
-- (0, 1)
doなしで書いたtest1'にそっくりであることがわかる
IxMonadは、Monadの一般化なので、
このように通常のStateモナドもIxMonadを使って表現することができる
通常のStateTとmethodの定義
code:hs
newtype StateT s m v = StateT { runStateT :: s -> m (v,s) }
instance (Monad m) => Monad (StateT s m) where
return x = StateT $ \s -> return (x, s)
bind m f = StateT $ \s -> do
(x, s') <- runStateT m s
runStateT (f x) s'
get :: (Monad m) => StateT s m s
get = state $ \s -> (s, s)
put :: (Monad m) => s -> StateT s m ()
put s = state $ \_ -> ((), s)
bindの型は、
StateT s m a -> (a -> StateT s m b) -> StateT s m b
actionの実行前後で、Stateの型は変わらずsである
Indexed Monadを使ってStateTを定義する
code:hs
newtype IxStateT m si so v = IxStateT { runIxStateT:: si -> m (v, so) }
instance Monad m => IxMonad (IxStateT m) where
ireturn x = IxStateT $ \si -> return (x, si)
ibind (IxStateT m) f = IxStateT $ \si -> do
(x, so) <- m si
runIxStateT (f x) so
iget :: Monad m => IxStateT m si si si
iget = IxStateT (\si -> return (si,si))
iput :: Monad m => so -> IxStateT m si so ()
iput s = IxStateT $ \_ -> return ((), s)
ibindの型は、
IxStateT m p q a -> (a -> IxStateT m q r b) -> IxStateT m p r b
igetの型は、IxStateT m si si si
igetしても、IxStateTが管理する状態は変わらないので、siのまま
iputの型は、so -> IxStateT m si so ()
soをiputすることで、IxStateTの管理する状態は、siからsoに変わる
Indexed State Monadの使用例
test2を、IxStateTを使って書く
code:hs
test3 :: IO ()
test3 = runIxStateT c 0 >>= print
where
c = iget ibind (
\v -> iput (succ v) ibind (
\_ -> ireturn v))
-- (0,1)
特に嬉しさもない、ただの比較のためのコードmrsekut.icon
IxStateの嬉しさの例
状態の初期値に0を取って、内部で1増やして、文字列に変換している
code:hs
test4 :: IO ()
test4 = runIxStateT c 0 >>= print
where
c = iget ibind (
\v -> iput (show $ succ v) ibind (
\_ -> ireturn v))
-- (0, "1")
RebindableSyntax拡張を使って、doで見やすく書くと
code:hs
{-# LANGUAGE RebindableSyntax #-} ...
test4' :: IO ()
test4' = runIxStateT c 0 >>= print
where
c = do
v <- iget
iput $ show $ succ v
return v
参考